/* kompilovano pres Turbo C 2.01 */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <graphics.h>
#include <string.h>
#include <dir.h>

int mk_usr=1;

/*
 *  get_file_length(FILE*)
 *  zjisti delku souboru v bajtech a vrati ji  
 */ 
int get_file_length(FILE *f) {
int pos;
int end;   

  pos = ftell(f); 
  fseek (f, 0, SEEK_END); 
  end = ftell(f);  
  fseek (f, pos, SEEK_SET); 

  return end;  
}

/*
 *  make_dir()
 *  vytvori slozku  <dir_name>
 */
void make_dir(const char *dir_name) {
  char *cmd_line = malloc(128);
  
  sprintf(cmd_line, "command.com /c IF NOT EXIST %s mkdir %s", dir_name, dir_name);
  system(cmd_line);
  free(cmd_line);
  return;
}

/*
 *  c_pause()
 *  provede pauzu pri warningu ci pri erroru pri kompilaci
 */
void c_pause(void) {
  printf("\r\n  >> Pokracujte stisknutim libovolne klavesy...\r\n");
  getchar();
}

/*
 *  c_log()
 *  vytiskne vysledek LOGu
 */
void c_log(const char *log) {
  int len = strlen(log);
  int i = 0;
  
  for(i=0; i<=len; i++) {
    if(log[i] == '\r' || log[i] == '\n')
      printf("\r\n");
    else
      printf("%c", log[i]);
  }
}

/*
 *  status(int)
 *  vytisne do konzole jak dopadl stav kompilace
 *  1 - OK
 *  2 - WARNING
 *  3 - ERROR 
 */ 
void status(int actual_position, char *log_result) {
int stat = 0;
int position = actual_position;
for(; position < 55; position++)
  putchar(' ');

  /* error */
  if(strstr(log_result, "Error source") || strstr(log_result, "Error include")) {
    stat = 3;
  }
  /* warning */
  else if(strstr(log_result, "Warning source") || strstr(log_result, "Warning include")) {
    stat = 2;
  }
  /* ok */
  else {
    stat = 1;
  }

  switch(stat) {
    case 1:
      textcolor(LIGHTGRAY);
      cprintf("[");
      textcolor(LIGHTGREEN);
      cprintf("  OK  ");
      textcolor(LIGHTGRAY);
      cprintf("]\r\n");
    break;
    case 2:
      textcolor(LIGHTGRAY);
      cprintf("[");
      textcolor(RED);
      cprintf(" WARN ");
      textcolor(LIGHTGRAY);
      cprintf("]\r\n");
      c_log(log_result);
      c_pause();
    break;
    case 3:
      textcolor(LIGHTGRAY);
      cprintf("[");
      textcolor(RED);
      cprintf("ERROR!");
      textcolor(LIGHTGRAY);
      cprintf("]\r\n");
      c_log(log_result);
      c_pause();
    break;
  }
}

/*
 *  compile()
 *  zkompiluje program <prj>
 */
void compile(const char *prj) {
  FILE *f_log;
  
  FILE *f_template;
  char *t_template   = malloc(1024);
  char *t_prj_file   = malloc(1024);
  char *t_filename   = malloc(64);
  
  char *cmd_line     = malloc(128);
  char *log_filename = malloc(128);
  char *log_result;
  int  delka;
  
      /* vygenerujeme soubor pro linkovani "app.ld" */
      /* nacteme */
      f_template = fopen("_tools\\_compile\\sablona\\app.ld", "r");
      fread(t_template, 52, 1, f_template);
      t_template[47] = '\0';
      fclose(f_template);
      
      /* ulozime */
      sprintf(t_filename, "tmp\\%s.ld", prj);
      sprintf(t_prj_file, t_template, prj, prj);
      f_template = fopen(t_filename, "w+");
      fwrite(t_prj_file, strlen(t_prj_file), 1, f_template);
      fclose(f_template);

      /********************************************************************/
      /* vytvorime prikaz */
      sprintf(cmd_line, "command.com /c _tools\\_compile\\bats\\compile.bat"
                          " %s > _tools\\_compile\\logs\\%s.log",
                            prj, prj);

      printf("Kompiluji aplikaci '%s'...", prj);
      system(cmd_line);
      
      /* vytvorime nazev souboru vytvoreneho logu */
      sprintf(log_filename, "_tools\\_compile\\logs\\%s.log", prj);

      f_log       = fopen(log_filename, "r");
      delka       = get_file_length(f_log);
      log_result  = malloc(delka+1);
      fread(log_result, 1, delka, f_log);
      status(strlen(prj)+24, log_result);
      free(log_result);
      fclose(f_log);
      
  /* uvolnime buffery */
  free(t_template);
  free(t_prj_file);
  free(t_filename);
  free(cmd_line);
}

/*
 *  compile_mod()
 *  zkompiluje modul <prj>
 */
void compile_mod(const char *prj) {
  FILE *f_log;
  
  FILE *f_template;
  char *t_template   = malloc(1024);
  char *t_prj_file   = malloc(1024);
  char *t_filename   = malloc(64);
  
  char *cmd_line     = malloc(128);
  char *log_filename = malloc(128);
  char *log_result;
  int  delka;
  
      /* vygenerujeme soubor pro linkovani "app.ld" */
      /* nacteme */
      f_template = fopen("_tools\\_compile\\sablona\\app.ld", "r");
      fread(t_template, 52, 1, f_template);
      t_template[47] = '\0';
      fclose(f_template);
      
      /* ulozime */
      sprintf(t_filename, "tmp\\%s.ld", prj);
      sprintf(t_prj_file, t_template, prj, prj);
      f_template = fopen(t_filename, "w+");
      fwrite(t_prj_file, strlen(t_prj_file), 1, f_template);
      fclose(f_template);

      /********************************************************************/
      /* vytvorime prikaz */
      sprintf(cmd_line, "command.com /c _tools\\_compile\\bats\\mcompile.bat"
                          " %s > _tools\\_compile\\logs\\%s.log",
                            prj, prj);

      printf("Kompiluji modul '%s'...", prj);
      system(cmd_line);
      
      /* vytvorime nazev souboru vytvoreneho logu */
      sprintf(log_filename, "_tools\\_compile\\logs\\%s.log", prj);

      f_log       = fopen(log_filename, "r");
      delka       = get_file_length(f_log);
      log_result  = malloc(delka+1);
      fread(log_result, 1, delka, f_log);
      status(strlen(prj)+21, log_result);
      free(log_result);
      fclose(f_log);
      
  /* uvolnime buffery */
  free(t_template);
  free(t_prj_file);
  free(t_filename);
  free(cmd_line);
}

/*
 *  create_project()
 *  vytvori projekt <prj> do /source/bin/*
 */  
void create_project(const char *prj) {
  FILE *f_template;
  char *t_template  = malloc(1024);
  char *t_ftemplate = malloc(1024);
  char *t_filename  = malloc(255);
  char *command     = malloc(255);
  
  textcolor(LIGHTBLUE);
  cprintf("\r\n- Vytvarim projekt '%s' -\r\n", prj);
  textcolor(LIGHTGRAY);
  
  
  /*  vytvorime slozku pro nas projekt  */
  sprintf(command, "mkdir source\\bin\\%s", prj);
  system(command);
  
  /* vytvorime C soubor pro nas projekt
      nacteme sablonu */
  f_template = fopen("_tools\\_compile\\sablona\\app.c", "r");
  fread(t_template, 200, 1, f_template);
  t_template[193] = '\0';
  fclose(f_template);
  
  /*  upravenou zapiseme  */
  sprintf(t_filename, "source\\bin\\%s\\%s.c", prj, prj);
  sprintf(t_ftemplate, t_template, prj);
  f_template = fopen(t_filename, "w+");
  fwrite(t_ftemplate, strlen(t_ftemplate), 1, f_template);
  fclose(f_template);
  printf("Vytvoren projekt '%s'", prj);
  
  free(t_template);
  free(t_ftemplate);
  free(t_filename);
  free(command);
}

/*
 *  create_module()
 *  vytvori modul <prj> do /source/mods/*
 */  
void create_module(const char *prj) {
  FILE *f_template;
  char *t_template  = malloc(1024);
  char *t_ftemplate = malloc(1024);
  char *t_filename  = malloc(255);
  char *command     = malloc(255);
  
  textcolor(LIGHTBLUE);
  cprintf("\r\n- Vytvarim modul '%s' -\r\n", prj);
  textcolor(LIGHTGRAY);
  
  
  /*  vytvorime slozku pro nas projekt  */
  sprintf(command, "mkdir source\\mods\\%s", prj);
  system(command);
  
  /* vytvorime C soubor pro nas projekt
      nacteme sablonu */
  f_template = fopen("_tools\\_compile\\sablona\\mod.c", "r");
  fread(t_template, 200, 1, f_template);
  t_template[193] = '\0';
  fclose(f_template);
  
  /*  upravenou zapiseme  */
  sprintf(t_filename, "source\\mods\\%s\\%s.c", prj, prj);
  sprintf(t_ftemplate, t_template, prj);
  f_template = fopen(t_filename, "w+");
  fwrite(t_ftemplate, strlen(t_ftemplate), 1, f_template);
  fclose(f_template);
  printf("Modul '%s' vytvoren", prj);
  
  free(t_template);
  free(t_ftemplate);
  free(t_filename);
  free(command);
}

/*
 *  make_user()
 *  vytvori noveho uzivatele
 */  
void make_user(void) {
  char *szUser = malloc(64);
  char *szPass = malloc(64);
  char *szCommand = malloc(256);
  
  if(!mk_usr) {
    system("command.com /c _tools\\_compile\\bats\\mkhs.bat root toor");
    printf("Uzivatel root (implicitni uzivatel) vytvoren...");
    free(szUser);
    free(szPass);
    free(szCommand);
    return;
  }
  printf("Vytvoreni uctu pro Altair OS\r\n"
         "Jmeno: ");
  gets(szUser);
  printf("Heslo: ");
  gets(szPass);
  sprintf(szCommand, "command.com /c _tools\\_compile\\bats\\mkhs.bat %s %s", szUser, szPass);
  system(szCommand);
  printf("Uzivatel %s vytvoren...", szUser);
  
  free(szUser);
  free(szPass);
  free(szCommand);
  return;
}

/*
 *  mk_mbr()
 *  zkompiluje bootloader
 */
void mk_mbr(void) {
  /*  KOMPILACE BOOTLOADERU  */
  textcolor(LIGHTBLUE);
  cprintf("\r\n- Kompilace bootloaderu -\r\n");
  textcolor(LIGHTGRAY);
  
  cprintf("Kompiluji bootloader...");
  system("command.com /c _tools\\nasm\\nasm.exe -f bin boot\\boot.asm -o tmp\\boot.bin");
  status(23, "null");
  return;
}  

/*
 *  build_kernel()
 *  zkompiluje pouze jadro
 */
void build_kernel(void) {
  FILE *f_log;
  char *log_filename = malloc(128);
  char *log_result;
  int delka;
  
  textcolor(LIGHTBLUE);
  cprintf("\r\n- Kompilace jadra -\r\n");
  textcolor(LIGHTGRAY);
  
  /*  KOMPILACE JADRA  */
  cprintf("Kompiluji jadro AltairOS...");
  system("command.com /c _tools\\_compile\\bats\\kernel.bat > _tools\\_compile\\logs\\kernel.log");

  f_log = fopen("_tools\\_compile\\logs\\kernel.log", "r");
  delka = get_file_length(f_log);
  log_result = malloc(delka+1);
  fread(log_result, 1, delka, f_log);
  status(27, log_result);
  fclose(f_log);
  free(log_filename);
  free(log_result);
  return;
}

/*
 *  mk_img()
 *  vytvori obraz
 */
void mk_img(void) {
  textcolor(LIGHTBLUE);
  cprintf("\r\n- Vytvarim image diskety... -\r\n");
  textcolor(LIGHTGRAY);

  system("mount X /mnt/aosflp");
  system("_tools\\_compile\\bats\\mkflp.bat");
  return;
}

/*
 *  build()
 *  sestavi cely operacni system
 */  
void build(void) {
  struct ffblk ffblk;
  int done;
  
  /********************************************/
  textcolor(LIGHTBLUE);
  cprintf("- Kompletni kompilace Altair OS -\r\n");
  textcolor(LIGHTGRAY);

  /* vytvorime slozky */
  cprintf("Vytvarim slozky pro vystupni aplikace...");
  system("command.com /c _tools\\_compile\\bats\\mk_dirs.bat");
  status(40, "null");

  /* ve slozce "source\bin" budeme hledat nazvy slozek (0x10), 
     ktere odpovidaji nazvum projektu */
  done = findfirst("source\\bin\\*", &ffblk, 0x10);
  while(!done) {
    if(*(ffblk.ff_name) != '.')
      compile(ffblk.ff_name);
    done = findnext(&ffblk);
  }
  
  /* ve slozce "source\mods" budeme hledat nazvy slozek (0x10), 
     ktere odpovidaji nazvum modulu */
  done = findfirst("source\\mods\\*", &ffblk, 0x10);
  while(!done) {
    if(*(ffblk.ff_name) != '.')
      compile_mod(ffblk.ff_name);
    done = findnext(&ffblk);
  }

  build_kernel();   /*  KOMPILACE JADRA         */
  mk_mbr();         /*  DOSAZENI MBR DO IMAGE   */
  make_user();      /*  VYTVORIME UZIVATELE     */  
  mk_img();         /*  DOSAZENI SOUBORU DO IMG */
  return;
}

/*********************************
 *    Application starts here:
 */ 
int main(int argc, char **argv) {
int print_msg = 1;
  /* bez parametru - postavime cely OS */
  if(argc == 1) {
    mk_usr=0;
    build();
  } else {
    /* parametr "-d" - jestli slozka neexistuje, vytvori ji */
    if(!strcmp(argv[1], "-d") && (argc == 3)) {
      make_dir(argv[2]);
      print_msg = 0;
    }
    /* parametr "-c" - zkompiluje jen vybrany program,
      ktery je uveden za parametrem "-c" */
    if(!strcmp(argv[1], "-c") && (argc == 3)) {
      if(!strcmp(argv[2], "kernel"))
        build_kernel();
      else
        compile(argv[2]);
    }
    /* parametr "-m" - zkompiluje jen vybrany modul,
      ktery je uveden za parametrem "-m" */
    if(!strcmp(argv[1], "-m") && (argc == 3)) {
      compile_mod(argv[2]);
    }
    /* parametr "-k" - zkompiluje pouze jadro
      (totez jako "-c kernel") */
    if(!strcmp(argv[1], "-k")) {
      build_kernel();
    }
    /* parametr "-i" - vytvori obraz */
    if(!strcmp(argv[1], "-i")) {
      mk_img();
    }
    /* parametr "-b" - vytvori bootloader */
    if(!strcmp(argv[1], "-b")) {
      mk_mbr();
    }
    /* parametr "-a" - provede kompletni build */
    if(!strcmp(argv[1], "-a")) {
      build();
    }
    /* parametr "-p" - vytvori novy projekt */
    if(!strcmp(argv[1], "-p") && (argc == 3)) {
      create_project(argv[2]);
    }
    /* parametr "-n" - vytvori novy modul */
    if(!strcmp(argv[1], "-n") && (argc == 3)) {
      create_module(argv[2]);
    }
    /* parametr "-u" - vytvori noveho uzivatele */
    if(!strcmp(argv[1], "-u")) {
      make_user();
    }
  }
  textcolor(LIGHTGRAY);
  if(print_msg) {
    cprintf("\r\n\n\nPro pokracovani v kompilaci provedte prikaz 'exit'...\r\n");
    getchar();
  }
  return 0;
}
